<?php

/**
 * This file is part of the Pagizer package.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright   Copyright (c) 2010 Advisa (http://www.advisa.fr)
 * @author      Pagizer Core Team <team@pagizer.org>
 * @package     Pagizer
 * @subpackage  backoffice
 */

class m_form_services_FormService extends f_core_Service
{
    /**
	 * @var m_form_services_FormService
	 */
	private static $instance;

    private $formName;
    private $theme = 'default';
    private $form;
    private $formValues = array();
    private $errors = array();
    private $context;

	/**
	 * Constructor
	 */
	private function __construct()
	{
		// Code if needed...
	}

	/**
	 * @return m_form_services_FormService
	 */
	public static function getInstance()
	{
		if (self::$instance === null)
		{
			self::$instance = new self();
		}
		return self::$instance;
	}

    public function setContextWebsite($website)
    {
        $this->context['website'] = $website;
        return $this;
    }

    public function setContextPage($page)
    {
        $this->context['page'] = $page;
        return $this;
    }

    public function getContextWebsite()
    {
        return $this->context['website'];
    }

    public function getContextPage()
    {
        return $this->context['page'];
    }

    /**
     *
     * @param <type> $docId
     * @param <type> $docLang
     */
    public function setStructure($docId, $docLang)
    {
        if($this->getDocumentProvider()->checkDocument($docId, $docLang))
        {
            $this->form = $this->getDocumentProvider()->getByUniqueId($docId, $docLang);

            if($this->form->getModel() == 'modules_form/structure')
            {
                $this->xml = $this->form->getStructure();
                $this->formName = f_xml_Xpath::getInstance()->fromString($this->xml)->fromXpath('form')->getAttributeValue('name');
            }
            else
            {
                $this->xml = '';
            }
        }
    }

    public function executeSuccessActions($formValues)
    {
        $actions = f_xml_Xpath::getInstance()->fromString($this->xml)->getElementsFromXpath('onSuccess/action');

        if(count($actions) > 0)
        {
            foreach($actions as $action)
            {
                if(!is_null($action->getAttributeValue('type')))
                {
                    $className = 'm_form_plugins_success_'.$action->getAttributeValue('type');
                }
                else
                {
                    if(!is_null($action->getAttributeValue('class')))
                    {
                        $className = $action->getAttributeValue('class');
                    }
                    else
                    {
                        die;
                    }
                }
                /* @var $action f_xml_XmlElement */
                $class = new $className();

                $params = $action->getSubElements('param');

                $class->setStructure($this->form);

                $class->setContextWebsite($this->getContextWebsite());
                $class->setContextPage($this->getContextPage());

                if(count($params) > 0)
                {
                    $parameters = array();
                    foreach($params as $param)
                    {
                        $parameters[$param->getAttributeValue('name')] = $param->getContent();
                    }
                    $class->setParameters($parameters);
                }
                $result = $class->setFormValues($formValues)->execute();

                if($result === false) die;
            }
        }
    }

    public function checkForm($formValues)
    {
        $this->formValues = $formValues;

        $fields = f_xml_Xpath::getInstance()->fromString($this->xml)->getElementsFromXpath('//field');

        foreach($fields as $field)
        {
            $fieldName = $field->getAttributeValue('name');
            /* @var $field f_xml_XmlElement */
            $checkers = $field->getSubElements('check');
            if(is_array($checkers) && count($checkers) > 0)
            {
                foreach($checkers as $checker)
                {
                    $type = $checker->getAttributeValue('type');

                    if(!is_null($type))
                    {
                        $className = 'm_form_plugins_checker_'.ucfirst($type);
                        $class = new $className();

                        $securityError = false;
                        if($type != 'security')
                        {
                            if(!isset($formValues[$fieldName])) $formValues[$fieldName] = null;
                            $class->setErrorMessage($checker->getAttributeValue('errormsg'));

                            $check = $class->check($formValues[$fieldName]);
                        }
                        else
                        {                            
                            if(!isset($formValues['h_o_n_e_y_p_o_t']) || !isset($formValues['timestamp']) || !isset($formValues['spinner']))
                            {
                                $check = false;
                                $securityError = true;
                            }
                            else
                            {
                                $values = array('formId' => $this->form->getId(),
                                                'honeypot' => $formValues['h_o_n_e_y_p_o_t'],
                                                'timestamp' => $formValues['timestamp'],
                                                'spinner' => $formValues['spinner'],
                                                'timeLimit' => $checker->getAttributeValue('timeLimit', '600'));

                                $check = $class->check($values);

                                if($check == false)
                                {
                                    $securityError = true;
                                }
                            }
                            
                        }

                        if($check != true)
                        {
                            if($securityError == true)
                            {
                                header('Location: '.URL_ABSOLUTE);
                            }

                            $this->errors[$fieldName] = $class->getErrorMessage();
                            break;
                        }
                    }
                    else
                    {
                        $class = $checker->getAttributeValue('class');
                        if(!is_null($class))
                        {

                        }
                    }
                }
            }
        }

    }

    public function hasErrors()
    {
        return count($this->errors) > 0;
    }

    public function getFormName()
    {
        return $this->formName;
    }

    /**
     * Set another theme for html generation
     * @param string $value
     * @return m_form_services_FormService
     */
    public function setTheme($value)
    {
        $this->theme = $value;
        return $this;
    }


// ########################################################################################### BUILD HTML FORM

    public function buildHtmlForm()
    {
        $result = '<div class="pz-block pz-block-form">';

        if(isset($this->xml) && !is_null($this->xml))
        {
            $xmlReader = new XMLReader();

            $xmlReader->XML($this->xml);

            while($xmlReader->read()) 
            {
                if($xmlReader->nodeType == XMLReader::ELEMENT)
                {
                    switch ($xmlReader->name)
                    {
                        case 'form':
                            $result .= $this->writeOpenForm($xmlReader);
                        break;
                        case 'fieldset':
                            $result .= $this->writeOpenFieldset($xmlReader);
                        break;
                        case 'label':
                            $result .= $this->writeLabel($xmlReader);
                        break;
                        case 'field':
                            $result .= $this->writeField($xmlReader);
                        break;
                        case 'submit':
                            $result .= $this->writeSubmit($xmlReader);
                        break;
                        default:
                            $result .= $this->writeTag($xmlReader);
                        break;
                     }
                }

                if($xmlReader->nodeType == XMLReader::END_ELEMENT)
                {
                    $result .= $this->writeCloseTag($xmlReader);
                }
            }
        }
        $result .= '</div>';
        return $result;
    }

    /**
     * Return new html form
     *
     * @param XMLReader $node
     */
    private function writeOpenForm($node)
    {
        $result = '';

        $theme = !is_null($node->getAttribute('theme')) ? $node->getAttribute('theme') : 'default';
        $this->setTheme($theme);

        if(!is_null($node->getAttribute('title'))) $result .= '<h1>'.$node->getAttribute('title').'</h1>';
        if(!is_null($node->getAttribute('description'))) $result .= '<p>'.$node->getAttribute('description').'</p>';

        $result .= '<form';

        if ($node->hasAttributes)
        {
            $exclude = array('title', 'description', 'method');
            while ($node->moveToNextAttribute())
            {
                if(!in_array($node->name, $exclude))
                {
                    $result .=  ' '.$node->name . "=\"" . $node->value . "\"";
                }
            }
            $node->moveToElement();
        }

        if(!is_null($node->getAttribute('method')))
        {
            $result .= ' method="'.$node->getAttribute('method').'"';
        }
        else
        {
            $result .= ' method="post"';
        }
        if(!is_null($node->getAttribute('name'))) $this->formName = $node->getAttribute('name');
        $result .= ' action=""';

        $result .= '>';

        return $result;
    }

    /**
     * Return new html form
     *
     * @param XMLReader $node
     */
    private function writeOpenFieldset($node)
    {
        $result = '<fieldset';

        if ($node->hasAttributes)
        {
            while ($node->moveToNextAttribute())
            {
                if($node->name != 'legend')
                {
                    $result .=  ' '.$node->name . "=\"" . $node->value . "\"";
                }
            }
            $node->moveToElement();
            $result .= '>';
        }

        $legend = $node->getAttribute('legend');

        if(!is_null($legend)) $result .= '<legend>'.$legend.'</legend>';

        return $result;
    }

    private function writeField($node)
    {
        $xml = f_xml_Xpath::getInstance()->fromString(trim($node->readOuterXML()));

        $attributes = $xml->getAttributes();

        $tplManager = f_template_Template::getInstance();

        $tplManager->setModuleTemplate('form', 'formFields'.DS.$this->theme.DS.$attributes['type'].'.html');

        if($attributes['type'] == 'security')
        {
            // see http://nedbatchelder.com/text/stopbots.html

            $timeStamp = time();
            $tplManager->assign('timestamp', $timeStamp);
            $key = crc32(m_backoffice_services_BackOfficeService::getKey());
            $spinner = md5($timeStamp.$_SERVER['REMOTE_ADDR'].$this->form->getId().$key);
            $tplManager->assign('spinner', $spinner);
        }
        else
        {
            if(isset($this->errors[$attributes['name']]))
            {
                $tplManager->assign('error', $this->errors[$attributes['name']]);
            }
            if(isset($this->formValues[$attributes['name']]))
            {
                $tplManager->assign('value', $this->formValues[$attributes['name']]);
            }
        }

        $tplManager->assign('attributes', $attributes);
        $tplManager->assign('formName', $this->formName);

        return $tplManager->render();
    }

    private function writeLabel($node)
    {
        return '<label>'.$node->readInnerXML().'</label>';
    }

    private function writeSubmit($node)
    {
        return '<button type="submit">'.$node->getAttribute('label').'</button>';
    }

    private function writeTag($node)
    {
        $unauthorized = array('check', 'onSuccess', 'action', 'param');

        $result = '';

        if(!in_array($node->name, $unauthorized))
        {
            $result .= '<'.$node->name;

            if ($node->hasAttributes)
            {
                while ($node->moveToNextAttribute())
                {
                    $result .=  ' '.$node->name . "=\"" . $node->value . "\"";
                }
                $node->moveToElement();
            }

            $result .= '>';

            if($node->isEmptyElement)
            {
                $result .= '</'.$node->name.'>';
            }

        }

        return $result;
    }

    private function writeCloseTag($node)
    {
        $unauthorized = array('check', 'onSuccess', 'action', 'param', 'field', 'submit');

        $result = '';

        if(!in_array($node->name, $unauthorized))
        {
            $result .= '</'.$node->name.'>';
        }

        return $result;
    }

    public function getFormStructure($formName, $template = 'default')
    {
        $tplManager = f_template_Template::getInstance();

        $tplManager->setModuleTemplate('form', 'forms/'.$template.'.xml');

        $tplManager->assign('formName', f_core_Routing::formatStringForUrl($formName));
        $tplManager->assign('formLabel', $formName);
        $tplManager->assign('website', $this->getContextWebsite());
        $tplManager->assign('page', $this->getContextPage());

        return $tplManager->render();
    }

    public function getFormTemplates()
    {
        $files = f_file_Resolver::getInstance()->getModuleFiles('form', 'templates/forms/*.xml');

        $result = array();

        foreach($files as $file)
        {
            $f = f_file_File::getInstance($file);
            if($f->getFileName() != 'default')
            {
                $res['name'] = $f->getFileName();
                $res['label'] = f_locale_Manager::getLocale('form.forms.tpl_'.$f->getFileName(), null, null, $f->getFileName());
                $result[] = $res;
            }
        }

        return $result;
    }
}